package com.ics.cmsadmin.frame.aop;

import com.ics.cmsadmin.frame.core.annotation.CacheDbMember;
import com.ics.cmsadmin.frame.core.annotation.ClearDbMember;
import com.ics.cmsadmin.frame.core.exception.ExceptionUtils;
import com.ics.cmsadmin.frame.core.bean.PageResult;
import com.ics.cmsadmin.frame.utils.GsonUtils;
import com.ics.cmsadmin.frame.utils.RedisUtils;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * redis缓存切面
 * Created by Administrator on 2017/6/23.
 */
@Aspect
@Component
@Log4j2
public class RedisCacheAop {

    @Pointcut("execution(@com.ics.cmsadmin.frame.core.annotation.ClearDbMember * com.ics.cmsadmin.modules.*.service.impl.*Impl.*(..))")
    public void clearCache(){}

    @Pointcut("execution(@com.ics.cmsadmin.frame.core.annotation.CacheDbMember * com.ics.cmsadmin.modules.*.service.impl.*Impl.*(..))")
    public void cache(){}

    @After("clearCache() && @annotation(clearDbMember)")
    public void clearCacheMember(ClearDbMember clearDbMember){
        log.info("进入 : clearCacheMember");
        String group = clearDbMember.group().name();
        RedisUtils.deleteGroup(group);
        log.info("清除缓存 :" + group);
        log.info("离开 : clearCacheMember");
    }

    @Around("cache() && @annotation(cacheDbMember)")
    public Object dbCacheMember(ProceedingJoinPoint pjd, CacheDbMember cacheDbMember){
        String shortString = pjd.getStaticPart().getSignature().toShortString();
        log.info("{} 进入 : dbCacheMember", shortString);
        String key = getUniquenessKey(pjd);
        String result = RedisUtils.get(cacheDbMember.group().name(), key);
        long startTime = System.currentTimeMillis();
        if (StringUtils.isNotBlank(result)){
            log.info("{} redis 缓存存在数据", shortString);
            Object resultObj = parseJson(result, pjd, cacheDbMember);
            if (resultObj != null){
                log.info("{} redis 缓存存在数据, 且转化成功", shortString);
                return resultObj;
            }else {
                log.info("{} redis 缓存存在数据, 但转化失败", shortString);
                RedisUtils.delete(cacheDbMember.group().name(), key);
            }
        }
        Object proceed = null;
        try {
            try {
                proceed = pjd.proceed();
            }catch (Exception e){
                log.error(e.getMessage());
            }
            log.info("{} 缓存不存在数据,查询数据库", shortString);
            if (proceed != null){
                RedisUtils.set(cacheDbMember.group().name(), key, GsonUtils.toJson(proceed), cacheDbMember.timeToLive());
            }
            return proceed;
        } catch (Throwable throwable) {
            log.error("{} 异常信息: {}" ,shortString, ExceptionUtils.collectExceptionStackMsg(throwable));
            return null;
        }finally {
            long endTime = System.currentTimeMillis();
            log.info("{} 离开 : dbCacheMember, 耗费时间(ms): {}", shortString, (endTime - startTime));
        }
    }

    @SuppressWarnings("unchecked")
    private Object parseJson(String result, ProceedingJoinPoint pjd, CacheDbMember cacheDbMember) {
        try {
            MethodSignature signature = (MethodSignature) pjd.getSignature();
            Class returnType = signature.getReturnType();
            if (returnType.isAssignableFrom(List.class)){
                Class resultClass = cacheDbMember.returnClass()[0];
                return GsonUtils.fromJson2List(result, resultClass);
            }else if (returnType.isAssignableFrom(Set.class)){
                Class resultClass = cacheDbMember.returnClass()[0];
                return GsonUtils.fromJson2Set(result,resultClass);
            }else if(returnType.isAssignableFrom(Map.class)){
                Class[] classes = cacheDbMember.returnClass();
                if (classes.length == 2){
                    Class keyClass = cacheDbMember.returnClass()[0];
                    Class valueClass = cacheDbMember.returnClass()[1];
                    return GsonUtils.fromJson2Map(result,keyClass,valueClass);
                }else{
                    Class valueClass = cacheDbMember.returnClass()[0];
                    return GsonUtils.fromJson2Map(result,valueClass);
                }
            }else if (returnType.isAssignableFrom(PageResult.class)){
                PageResult pageResult = GsonUtils.fromJson2Bean(result, PageResult.class);
                Class resultClass = cacheDbMember.returnClass()[0];
                List dataList = pageResult.getDataList();
                pageResult.setDataList(GsonUtils.fromJson2List(GsonUtils.toJson(dataList), resultClass));
                return pageResult;
            }else{
                Class resultClass = cacheDbMember.returnClass()[0];
                return GsonUtils.fromJson2Bean(result, resultClass);
            }
        }catch (Exception e){
            return null;
        }

    }
    private String getUniquenessKey(ProceedingJoinPoint pjd){
        String key = pjd.getStaticPart().getSignature().toLongString() + GsonUtils.toJson(pjd.getArgs());
        String shortString = pjd.getSignature().toShortString();
        shortString = shortString.replace("(..)", "");
        return String.format("%s:%s", shortString, DigestUtils.md5Hex(key));
    }
}
